﻿//created by Boguste
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
using System.Xml.Linq;
using Mono.Cecil.Cil;

namespace Gendarme.Rules.Smells {
	public class CommanderSerializer {

		public const string AFFECTED_CLASS = "affectedclass";
		public const string AFFECTED = "affected";
		public const string NAME = "Name";
		public const string STATEMENT = "statement";
		public const string ORIGIN = "origin";
		public const string NUMBER = "number";
		public const string VARIANT = "variant";

		public static XElement SerializeCodeDuplication(MethodDefinition meth1, MethodDefinition meth2) {
			if (meth1.DeclaringType.Equals(meth2.DeclaringType)) {
				return SerializeSameClassCodeDuplication(meth1, meth2);
			}
			return null;
		}

		public static XElement SerializeSpeculativeGenerality(TypeDefinition baseClass, TypeDefinition inheritedClass) {

			return new XElement(AFFECTED_CLASS,
													new XAttribute(NAME, baseClass.Name),
													new XAttribute(VARIANT, inheritedClass.Name));
		}

		private static XElement SerializeSameClassCodeDuplication(MethodDefinition meth1, MethodDefinition meth2) {
			var meth1Stmts = GetStatements(meth1);
			var meth2Stmts = GetStatements(meth2);
			var affectedClass = new XElement(AFFECTED_CLASS,
																			new XAttribute(NAME, meth1.DeclaringType.Name));

			for (int i = 0; i < meth1Stmts.Count; i++) {
				for (int j = 0; j < meth2Stmts.Count; j++) {
					if (InstructionMatcher.AreEquivalent(meth1Stmts[i], meth2Stmts[j])) {
						affectedClass.Add(new XElement(AFFECTED,
						new XElement(STATEMENT, new XAttribute(ORIGIN, meth1.Name), new XAttribute(NUMBER, i)),
						new XElement(STATEMENT, new XAttribute(ORIGIN, meth2.Name), new XAttribute(NUMBER, j))));
					}
				}
			}

			return affectedClass;
		}

		public static XElement SerializeLongMethodChain(MethodDefinition meth) {

			return new XElement(NAME, new XAttribute(NAME, meth.DeclaringType.Name));
		}

		private static IList<Instruction> GetStatements(MethodDefinition current) {
			var stmts = new List<Instruction>();
			var instrs = current.Body.Instructions.OfType<Instruction>().ToList();

			instrs.RemoveAt(0);//remove method declaration statement
			instrs.RemoveAt(instrs.Count - 1);//remove closing bracket statement

			int prevLine = -1;
			int magic = 16707566;
			foreach (var inst in instrs) {
				if (inst.SequencePoint != null) {
					if ((inst.SequencePoint.StartLine > prevLine) && (inst.SequencePoint.StartLine != magic)) {
						prevLine = inst.SequencePoint.StartLine;
						stmts.Add(inst);
					}
				}

			}

			return stmts;
		}
	}

	//public enum SmellType { Undefined = 0, 
	//  SameClassDuplication = 1,
	//  SiblingClassDuplication=2

	//}
}
